package com.calm.clibrary.andswipeback;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.Window;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.inputmethod.InputMethodManager;

import java.lang.ref.WeakReference;

/**
 * Create by Calm
 * 2020/1/11 11:41
 */
public class AndSwipeBackHelper {
    //点击事件
    private static final int STATE_ACTION_DOWN = 1;
    //点击抬起
    private static final int STATE_ACTION_UP = 2;
    //开始滑动，不返回前一页面
    private static final int STATE_BACK_START = 3;
    //结束滑动，不返回前一页面
    private static final int STATE_BACK_FINISH = 4;
    //开始滑动，返回前一页面
    private static final int STATE_FORWARD_START = 5;
    //结束滑动，返回前一页面
    private static final int STATE_FORWARD_FINISH = 6;
    //默认触摸区间
    private static final int DEFAULT_EDGE_SIZE = 20;
    private Activity mCurrentActivity;
    private ViewManager mViewManager;
    //触发移动事件的最小距离
    private int mTouchSlop;
    //触摸区间dp
    private int mEdgeSize;
    private ValueAnimator mValueAnimator;
    private AndOnSlideFinishListener mSlideFinishListener;
    //是否支持滑动返回 默认支持
    private boolean mEnableSwipeBack = true;
    //滑动动画正在展示中
    private boolean mIsSlideAnimPlaying;
    //记录手势上次在屏幕的位置X
    private float mLastPointX;
    //是否在触摸区间
    private boolean mIsInThresholdArea;
    //是否正在滑动
    private boolean mIsSliding;
    //当前滑动距离
    private float mDistanceX;
    private Interpolator mInterpolator = new DecelerateInterpolator(2f);
    public AndSwipeBackHelper(Activity currentActivity,AndSlideActivityCallback callback){
        mCurrentActivity = currentActivity;
        mViewManager = new ViewManager(currentActivity,callback);
        mTouchSlop = ViewConfiguration.get(mCurrentActivity).getScaledTouchSlop();
        float density = mCurrentActivity.getResources().getDisplayMetrics().density;
        mEdgeSize = (int) (DEFAULT_EDGE_SIZE * density + 0.5f);
    }

    /**
     * 此方法用以处理触摸事件
     * @param event
     * @return
     */
    public boolean processTouchEvent(MotionEvent event){
        //如果不支持滑动返回，事件交由View处理
        if(!mEnableSwipeBack){
            return false;
        }
        //如果滑动正在进行，则直接消费此次事件
        if(mIsSlideAnimPlaying){
            return true;
        }
        int action = event.getAction() & MotionEvent.ACTION_MASK;
        if(action == MotionEvent.ACTION_DOWN){
            mLastPointX = event.getRawX();
            mIsInThresholdArea = mLastPointX > 0 && mLastPointX <= mEdgeSize;
        }
        //不在触摸区间，则交由View处理
        if(!mIsInThresholdArea){
            return false;
        }
        int actionIndex = event.getActionIndex();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                changeStatus(STATE_ACTION_DOWN);
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                //有第二个手势加入，正在滑动，则消费该次事件
                if(mIsSliding){
                    return true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                float curPointX = event.getRawX();
                boolean originSlideStatus = mIsSliding;
                if(!originSlideStatus){
                    //是否满足滑动的条件
                    if(Math.abs(curPointX - mLastPointX) < mTouchSlop){
                        return false;
                    }else {
                        mIsSliding = true;
                    }
                }
                if(actionIndex == 0){
                    //开始滑动
                    float distanceX = curPointX - mLastPointX;
                    mLastPointX = curPointX;
                    setTranslationX(mDistanceX + distanceX);
                    if(originSlideStatus == mIsSliding){
                        return true;
                    }else {
                        //首次判断为滑动需修正事件，手动改事件为ACTION_CANCEL，并通知底层View
                        MotionEvent cancelEvent = MotionEvent.obtain(event);
                        cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
                        mCurrentActivity.getWindow().superDispatchTouchEvent(cancelEvent);
                        return true;
                    }
                }else {
                    return true;
                }
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_POINTER_UP:
            case MotionEvent.ACTION_OUTSIDE:
                if(mDistanceX == 0){
                    //没有进行滑动
                    mIsSliding = false;
                    changeStatus(STATE_ACTION_UP);
                    return false;
                }
                if(mIsSliding && actionIndex == 0){
                    //取消滑动  手势抬起 为第一手势  开始滑动动画
                    mIsSliding = false;
                    changeStatus(STATE_ACTION_UP);
                    return true;
                }else if(mIsSliding){
                    return true;
                }
                break;
            default:
                mIsSliding = false;
                break;

        }
        return false;
    }

    /**
     * 设置滑动
     * @param x
     */
    private void setTranslationX(float x){
        int width = mCurrentActivity.getResources().getDisplayMetrics().widthPixels;
        mDistanceX = x;
        mDistanceX = Math.max(0,mDistanceX);
        mDistanceX = Math.min(width,mDistanceX);
        mViewManager.translateView(mDistanceX,width);
    }
    /**
     * 处理事件，不包括滑动
     * @param status
     */
    private void changeStatus(int status){
        switch (status){
            case STATE_ACTION_DOWN:
                //如果有弹出键盘，则收起键盘
                InputMethodManager inputMethod = (InputMethodManager) mCurrentActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
                View view = mCurrentActivity.getCurrentFocus();
                if(view != null && inputMethod != null){
                    inputMethod.hideSoftInputFromWindow(view.getWindowToken(),0);
                }
                if(!mViewManager.prepareViews()){
                    return;
                }
                break;
            case STATE_ACTION_UP:
                int width = mCurrentActivity.getResources().getDisplayMetrics().widthPixels;
                if(mDistanceX == 0){
                    mViewManager.clearView(false);
                }else if(mDistanceX > width / 4){
                    changeStatus(STATE_FORWARD_START);
                }else {
                    changeStatus(STATE_BACK_START);
                }
                break;
            case STATE_BACK_START:
                mIsSlideAnimPlaying = true;
                startBackAnim();
                break;
            case STATE_BACK_FINISH:
                mDistanceX = 0;
                mIsSliding = false;
                mViewManager.clearView(false);
                break;
            case STATE_FORWARD_START:
                mIsSlideAnimPlaying = true;
                startForwardAnim();
                break;
            case STATE_FORWARD_FINISH:
                mViewManager.clearView(true);
                if(mSlideFinishListener != null){
                    mSlideFinishListener.onFinish();
                }
                break;

        }
    }
    private void startBackAnim(){
        final int maxValue = 150;
        mValueAnimator = new ValueAnimator();
        mValueAnimator.setInterpolator(mInterpolator);
        mValueAnimator.setIntValues(0,maxValue);
        mValueAnimator.setDuration(maxValue);
        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                Integer value = (Integer) valueAnimator.getAnimatedValue();
                float currentDistanceX = mDistanceX * (maxValue - value) / maxValue;
                setTranslationX(currentDistanceX);
            }
        });
        mValueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                animation.removeListener(this);
                mIsSlideAnimPlaying = false;
                changeStatus(STATE_BACK_FINISH);
            }
        });
        mValueAnimator.start();
    }
    private void startForwardAnim(){
        final int maxValue = 300;
        mValueAnimator = new ValueAnimator();
        mValueAnimator.setInterpolator(mInterpolator);
        mValueAnimator.setIntValues(0,maxValue);
        mValueAnimator.setDuration(maxValue);
        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                Integer value = (Integer) valueAnimator.getAnimatedValue();
                int width = mCurrentActivity.getResources().getDisplayMetrics().widthPixels;
                float currentDistanceX = mDistanceX + (width - mDistanceX) * value / maxValue;
                setTranslationX(currentDistanceX);
            }
        });
        mValueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                animation.removeListener(this);
                changeStatus(STATE_FORWARD_FINISH);
            }
        });
        mValueAnimator.start();
    }
    /**
     * 释放资源
     */
    public void finishSwipeImmediately(){
        if(mValueAnimator != null){
            mValueAnimator.cancel();
            mValueAnimator = null;
        }
    }
    public void setOnSlideFinishListener(AndOnSlideFinishListener listener){
        this.mSlideFinishListener = listener;
    }
    public interface AndSlideBackManager{
        /**
         * 自身页面是否支持滑动返回
         * @return
         */
        boolean isSupportSlideBack();

        /**
         * 其它页面能否滑动返回至当前页面
         * @return
         */
        boolean canSlideBackToUs();
    }

    /**
     * 当滑动完成回调
     */
    public interface AndOnSlideFinishListener{
        void onFinish();
    }
    private static class ViewManager{
        private Activity mCurrentActivity;
        private WeakReference<Activity> mPreviousActivity;
        private AndSlideActivityCallback mCallback;
        private ViewGroup mCurrentContentView;
        private View mDisplayView;
        private View mPreviousDisplayView;
        private TempViewGroup mTempViewGroup;
        private ViewManager(Activity currentActivity,AndSlideActivityCallback callback){
            mCurrentActivity = currentActivity;
            mCallback = callback;
        }

        /**
         * 准备相关View
         * @return
         */
        private boolean prepareViews(){
            //根window
            mCurrentContentView = mCurrentActivity.findViewById(Window.ID_ANDROID_CONTENT);
            //前一个Activity
            Activity previousActivity = mCallback.getPreviousActivity();
            //前一个页面为空或不支持滑动返回
            if(previousActivity == null ||
                    (previousActivity instanceof AndSlideBackManager && ! ((AndSlideBackManager)previousActivity).canSlideBackToUs())){
                mCurrentContentView = null;
                return false;
            }
            //根view
            mDisplayView = mCurrentContentView.getChildAt(0);
            ViewGroup previousContentView = previousActivity.findViewById(Window.ID_ANDROID_CONTENT);
            mPreviousDisplayView = previousContentView.getChildAt(0);
            //前一个页面的根布局为空
            if(mPreviousDisplayView == null){
                mCurrentContentView = null;
                mDisplayView = null;
                return false;
            }
            mPreviousActivity = new WeakReference<>(previousActivity);
            //将前一个页面的view移除
            previousContentView.removeView(mPreviousDisplayView);
            mTempViewGroup = new TempViewGroup(mCurrentActivity);
            mTempViewGroup.addPreviousView(mCurrentContentView,previousContentView,mPreviousDisplayView);
            //将临时viewGroup添加到当前页面的下面
            mCurrentContentView.addView(mTempViewGroup,0);
            return true;
        }
        private void translateView(float x,int screentW){
            if(mCurrentContentView == null){
                return;
            }
            mTempViewGroup.setDistancX(x);
            mPreviousDisplayView.setX((-screentW+x)/3);
            if(mDisplayView != null){
                mDisplayView.setX(x);
            }
        }
        private void clearView(boolean forward){
            if(mCurrentContentView == null){
                return;
            }
            mPreviousDisplayView.setX(0);
            ViewGroup previousContentView = null;
            if(mPreviousActivity != null && mPreviousActivity.get() != null
                && !mPreviousActivity.get().isFinishing()){
                previousContentView = mPreviousActivity.get().findViewById(Window.ID_ANDROID_CONTENT);
            }
            mTempViewGroup.clearPreviousView(previousContentView,mPreviousDisplayView);
            if(!forward){
                mCurrentContentView.removeView(mTempViewGroup);
            }
            mTempViewGroup = null;
            mPreviousDisplayView = null;
            mCurrentContentView = null;
            mDisplayView = null;
        }
    }
}
